import uuid
from datetime import timedelta, datetime
from typing import Optional

from fastapi import FastAPI, HTTPException, Depends, status, Form, UploadFile, File, Query
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
from fastapi.staticfiles import StaticFiles
from jose import JWTError, jwt

from seetaface.api import *
from config import settings
from src.database.database import get_db, SessionLocal, init_db
from src.models.model import User, FaceRecord
from src.schemas.schemas import Token, TokenData, UserCreate, UserResponse, FaceRecognizeResult, FaceRegisterResponse, \
    UserUpdate, UserListResponse, FaceRecordListResponse
from src.utils.logging import log
from src.utils.utils import (
    verify_password, get_password_hash, create_access_token,
    save_face_vector, search_face_vector, delete_face_vector, save_face_image, save_results_face_image
)
from PIL import Image, ImageDraw, ImageFont

# 全局字体变量
try:
    GLOBAL_FONT = ImageFont.truetype("simhei.ttf", 30)  # 黑体
except:
    # 如果找不到字体文件，使用默认字体（可能不支持中文）
    GLOBAL_FONT = ImageFont.load_default()
# 初始化SeetaFace
init_mask = FACE_DETECT | FACERECOGNITION | LANDMARKER5
seeta_face = SeetaFace(init_mask)

app = FastAPI(
    title="Face API",
    description="高性能人脸识别API服务",
    version="1.0.0"
)

# 静态文件
app.mount("/static", StaticFiles(directory=settings.STATIC_DIR), name="static")

oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")


# 依赖项
async def get_current_user(token: str = Depends(oauth2_scheme), db=Depends(get_db)):
    credentials_exception = HTTPException(
        status_code=status.HTTP_401_UNAUTHORIZED,
        detail="无法验证凭据",
        headers={"WWW-Authenticate": "Bearer"},
    )
    try:
        payload = jwt.decode(token, settings.SECRET_KEY, algorithms=[settings.ALGORITHM])
        username: str = payload.get("sub")
        if username is None:
            raise credentials_exception
        token_data = TokenData(username=username)
    except JWTError:
        raise credentials_exception

    user = db.query(User).filter(User.username == token_data.username).first()
    if user is None:
        raise credentials_exception
    return user


async def get_current_active_user(current_user: User = Depends(get_current_user)):
    if not current_user.is_active:
        raise HTTPException(status_code=400, detail="当前用户未激活")
    return current_user


def get_user(db, username: str):
    return db.query(User).filter(User.username == username).first()


def authenticate_user(db, username: str, password: str):
    user = get_user(db, username)
    if not user:
        raise HTTPException(status_code=404, detail="用户不存在")
    if not verify_password(password, user.hashed_password):
        raise HTTPException(status_code=400, detail="用户名或者密码错误")
    return user


# 路由
@app.post("/token", response_model=Token)
async def login_for_access_token(
        form_data: OAuth2PasswordRequestForm = Depends(),
        db: SessionLocal = Depends(get_db)
):
    user = authenticate_user(db, form_data.username, form_data.password)
    if not user:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="用户名或者密码错误",
            headers={"WWW-Authenticate": "Bearer"},
        )
    access_token_expires = timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES)
    access_token = create_access_token(
        data={"sub": user.username}, expires_delta=access_token_expires
    )
    return {"access_token": access_token, "token_type": "bearer"}


@app.post("/user", response_model=UserResponse)
async def create_user(
        user: UserCreate,
        db: SessionLocal = Depends(get_db)
):
    """
    新增用户
    :param user: {"username":"admin","password":"admin"}
    :param db:
    :return:
    """
    db_user = db.query(User).filter(User.username == user.username).first()
    if db_user:
        raise HTTPException(status_code=400, detail="用户已经存在")

    hashed_password = get_password_hash(user.password)
    db_user = User(id=str(uuid.uuid4()).replace("-", ""), username=user.username, hashed_password=hashed_password,
                   created_at=datetime.now())
    db.add(db_user)
    db.commit()
    db.refresh(db_user)
    log.info(f"新增用户: {db_user.username}")
    return db_user


@app.put("/user/{id}", response_model=UserResponse)
async def update_user(
        id: str,
        user_update: UserUpdate,
        current_user: User = Depends(get_current_active_user),
        db: SessionLocal = Depends(get_db)
):
    """
    修改密码，修改激活状态
    :param id: 用户id
    :param user_update: {"password","is_active",True}
    :param current_user: 当前操作人
    :param db:
    :return:
    """
    db_user = db.query(User).filter(User.id == id).first()
    if not db_user:
        raise HTTPException(status_code=404, detail="用户不存在")

    update_data = user_update.model_dump(exclude_unset=True)
    if "password" in update_data:
        hashed_password = get_password_hash(update_data["password"])
        update_data["hashed_password"] = hashed_password
        del update_data["password"]

    for field, value in update_data.items():
        setattr(db_user, field, value)
    log.info(f"{current_user.username}修改了用户信息: {db_user.username}")
    db.commit()
    db.refresh(db_user)
    return db_user


@app.delete("/user/{id}")
async def delete_user(
        id: str,
        current_user: User = Depends(get_current_active_user),
        db: SessionLocal = Depends(get_db)
):
    """
    删除用户
    :param id: 用户id
    :param current_user:
    :param db:
    :return:
    """
    db_user = db.query(User).filter(User.id == id).first()
    if not db_user:
        raise HTTPException(status_code=404, detail="用户不存在")
    log.info(f"{current_user.username}删除了用户: {db_user.username}")
    db.delete(db_user)
    db.commit()
    return {"status": "success", "id": id}


@app.get("/user", response_model=UserListResponse)
async def list_users(
        page: int = Query(1, ge=1),
        page_size: int = Query(10, ge=1, le=100),
        username: Optional[str] = None,
        is_active: Optional[bool] = None,
        current_user: User = Depends(get_current_active_user),
        db: SessionLocal = Depends(get_db)
):
    """获取用户列表(分页)"""
    query = db.query(User)

    # 添加过滤条件
    if username:
        query = query.filter(User.username.ilike(f"%{username}%"))
    if is_active is not None:
        query = query.filter(User.is_active == is_active)

    # 计算总数
    total = query.count()

    # 获取分页数据
    items = query.order_by(User.created_at.desc()) \
        .offset((page - 1) * page_size) \
        .limit(page_size) \
        .all()

    return {
        "data": items,
        "pagination": {
            "page": page,
            "page_size": page_size,
            "total": total
        }
    }


@app.post("/face/register", response_model=FaceRegisterResponse)
async def register_face(
        user_id: int = Form(...),
        user_name: str = Form(...),
        image: UploadFile = File(...),
        current_user: User = Depends(get_current_active_user),
        db: SessionLocal = Depends(get_db)
):
    """注册新人脸到人脸库"""
    try:
        # 保存到数据库
        face_record = db.query(FaceRecord).filter(FaceRecord.user_id == user_id).first()
        if face_record:
            raise HTTPException(status_code=400, detail="用户id已经注册过人脸")
        # 读取图片内容
        image_data = await image.read()
        # 保存图片到静态目录
        image_url = save_face_image(user_id, image_data)

        # 处理图片并提取特征
        nparr = np.frombuffer(image_data, np.uint8)
        img = cv2.imdecode(nparr, cv2.IMREAD_COLOR)
        # 检查图像是否正确读取
        if img is None:
            raise HTTPException(status_code=400, detail="图片不可用")

        # 检测人脸 - 使用OpenCV读取的图像
        detect_result = seeta_face.Detect(img)
        if not detect_result:
            raise HTTPException(status_code=400, detail="未检测到人脸")

        # 提取特征
        face_pos = detect_result.data[0].pos
        points = seeta_face.mark5(img, face_pos)
        features = seeta_face.Extract(img, points)
        face_record = FaceRecord(
            id=str(uuid.uuid4()).replace("-", ""),
            user_id=user_id,
            user_name=user_name,
            image_url=image_url,
            created_user=current_user.username,
            updated_user=current_user.username
        )
        db.add(face_record)
        db.commit()
        if not isinstance(features, (np.ndarray, list)):
            features = np.ctypeslib.as_array(features)
        # 保存特征向量
        save_face_vector(user_id, features)

        log.info(f"人脸注册成功，用户ID: {user_id}")
        return {
            "status": "success",
            "user_id": user_id,
            "image_url": image_url
        }

    except HTTPException:
        raise
    except Exception as e:
        log.error(f"人脸注册失败: {str(e)}", exc_info=True)
        raise HTTPException(status_code=500, detail=str(e))


@app.put("/face/update", response_model=FaceRegisterResponse)
async def update_face(
        user_id: int = Form(...),
        user_name: str = Form(...),
        image: UploadFile = File(...),
        current_user: User = Depends(get_current_active_user),
        db: SessionLocal = Depends(get_db)
):
    """更新人脸库中的用户信息"""
    try:
        face_record = db.query(FaceRecord).filter(FaceRecord.user_id == user_id).first()
        if not face_record:
            raise HTTPException(status_code=404, detail="用户不存在")

        # 读取图片内容
        image_data = await image.read()
        # 保存图片到静态目录
        image_url = save_face_image(user_id, image_data)

        # 处理图片并提取特征
        nparr = np.frombuffer(image_data, np.uint8)
        img = cv2.imdecode(nparr, cv2.IMREAD_COLOR)

        # 检查图像是否正确读取
        if img is None:
            raise HTTPException(status_code=400, detail="图片不可用")

        # 检测人脸 - 使用OpenCV读取的图像
        detect_result = seeta_face.Detect(img)
        if not detect_result:
            raise HTTPException(status_code=400, detail="未检测到人脸")

        # 提取特征
        face_pos = detect_result.data[0].pos
        points = seeta_face.mark5(img, face_pos)
        features = seeta_face.Extract(img, points)
        # 可以添加类型检查和转换
        if not isinstance(features, (np.ndarray, list)):
            features = np.ctypeslib.as_array(features)
        # 保存特征向量
        save_face_vector(user_id, features)

        # 更新其他信息
        if user_name:
            face_record.user_name = user_name
        if current_user.username:
            face_record.updated_user = current_user.username
        if image_url:
            face_record.image_url = image_url
        face_record.updated_at = datetime.now()
        db.commit()

        log.info(f"人脸更新成功，用户ID: {user_id}")
        return {
            "status": "success",
            "user_id": user_id,
            "image_url": image_url
        }
    except HTTPException:
        raise
    except Exception as e:
        log.error(f"人脸更新失败: {str(e)}")
        raise HTTPException(status_code=500, detail=str(e))


@app.delete("/face/delete/{user_id}")
async def delete_face(
        user_id: int,
        current_user: User = Depends(get_current_active_user),
        db: SessionLocal = Depends(get_db)
):
    """从人脸库中删除用户"""
    try:
        face_record = db.query(FaceRecord).filter(FaceRecord.user_id == user_id).first()
        if not face_record:
            raise HTTPException(status_code=404, detail="User not found")

        # 从Milvus删除
        delete_face_vector(user_id)
        # 从数据库删除
        db.delete(face_record)
        db.commit()

        log.info(f"{current_user.username} 删除了人脸信息： {user_id}")
        return {"status": "success", "user_id": user_id}
    except HTTPException:
        raise
    except Exception as e:
        log.error(f"删除人脸失败: {str(e)}")
        raise HTTPException(status_code=500, detail=str(e))


@app.post("/face/recognize", response_model=List[FaceRecognizeResult])
async def recognize_face(
        image: UploadFile = File(...),
        threshold: float = 0.6,
        current_user: User = Depends(get_current_active_user),
        db: SessionLocal = Depends(get_db)
):
    try:
        contents = await image.read()
        nparr = np.frombuffer(contents, np.uint8)
        img = cv2.imdecode(nparr, cv2.IMREAD_COLOR)

        if img is None:
            raise HTTPException(status_code=400, detail="图片不可用")

        detect_result = seeta_face.Detect(img)
        if not detect_result:
            raise HTTPException(status_code=204, detail="未检测到人脸")
        results = []
        # 转换颜色空间，用于绘制中文
        img_pil = Image.fromarray(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
        draw = ImageDraw.Draw(img_pil)
        # 遍历所有检测到的人脸
        for i in range(detect_result.size):
            face_info = detect_result.data[i]
            # 获取人脸位置信息
            face_rect = face_info.pos
            points = seeta_face.mark5(img, face_rect)
            features = seeta_face.Extract(img, points)
            # 可以添加类型检查和转换
            if not isinstance(features, (np.ndarray, list)):
                features = np.ctypeslib.as_array(features)  # 如果是ctypes数组
            matches = search_face_vector(features, threshold=threshold)
            if not matches:
                log.info("未匹配到人脸")
                continue
            # 获取最相似的匹配
            best_match = max(matches, key=lambda x: x[1])  # 返回格式为(user_id, features,similarity)
            user_id, max_similarity, features1, = best_match
            log.info(f"matches size {len(matches)} Best match for user {user_id} with similarity {max_similarity}")
            face_record = db.query(FaceRecord).filter(FaceRecord.user_id == user_id).first()
            if face_record:
                similarity = seeta_face.compare_feature_np(features, features1)
                if similarity > threshold:
                    # 绘制人脸框（使用PIL）
                    draw.rectangle(
                        [(face_rect.x, face_rect.y),
                         (face_rect.x + face_rect.width, face_rect.y + face_rect.height)],
                        outline=(255, 0, 0),  # 红色框
                        width=2
                    )

                    # 绘制中文姓名（使用全局字体）
                    text = f"{face_record.user_name} {similarity:.2f}"
                    draw.text(
                        (face_rect.x, face_rect.y - 35),  # 在人脸框上方显示
                        text,
                        font=GLOBAL_FONT,  # 使用全局字体
                        fill=(0, 0, 255)  # 蓝色文字
                    )
                    img_with_text = cv2.cvtColor(np.array(img_pil), cv2.COLOR_RGB2BGR)
                    result_url = save_results_face_image(user_id, img_with_text)
                    results.append(FaceRecognizeResult(
                        user_id=int(face_record.user_id),
                        user_name=str(face_record.user_name),
                        similarity=float(similarity),
                        image_url=face_record.image_url,
                        result_url=result_url
                    ))

        if not results:
            raise HTTPException(status_code=204, detail="未匹配到人脸")
        return results
    except HTTPException:
        raise
    except Exception as e:
        log.error(f"Recognition failed: {str(e)}")
        raise HTTPException(status_code=500, detail=str(e))


# 人脸记录分页查询
@app.get("/face/records", response_model=FaceRecordListResponse)
async def list_face_records(
        page: int = Query(1, ge=1),
        page_size: int = Query(10, ge=1, le=100),
        user_id: Optional[int] = None,
        user_name: Optional[str] = None,
        current_user: User = Depends(get_current_active_user),
        db: SessionLocal = Depends(get_db)
):
    """获取人脸记录列表(分页)"""
    query = db.query(FaceRecord)

    if user_id:
        query = query.filter(FaceRecord.user_id == user_id)
    if user_name:
        query = query.filter(FaceRecord.user_name.ilike(f"%{user_name}%"))

    total = query.count()
    items = query.order_by(FaceRecord.created_at.desc()).offset((page - 1) * page_size).limit(page_size).all()

    return {
        "data": items,
        "pagination": {
            "page": page,
            "page_size": page_size,
            "total": total
        }
    }


if __name__ == "__main__":
    import uvicorn

    init_db()
    uvicorn.run(
        app,
        host="0.0.0.0",
        port=8000,
        log_config=None  # 使用loguru替代默认日志
    )
